package com.iscreen.utils.db.query;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.log4j.Logger;

import com.iscreen.utils.constants.Constants;
import com.iscreen.utils.db.connection.DBErrorHandler;
import com.iscreen.utils.exceptions.IScreenException;
import com.iscreen.utils.exceptions.IScreenExceptionCodes;

public class Query
{
	private PreparedStatement statement;
	private ResultSet rs;
	private DBErrorHandler errorHandler;
	private String sql;
	
	private Logger logger = Logger.getLogger(Query.class);
	
	public Query(Connection connection, String sql, DBErrorHandler errorHandler) throws IScreenException
	{
		this.errorHandler = errorHandler;
		this.sql = sql;
		try
		{
			statement = connection.prepareStatement(sql);
		}
		catch (SQLException e)
		{
			handleException(e);
		}
	}
	
	public void setParameter(int index, Object value) throws IScreenException
	{
		try
		{
			statement.setObject(index, value);
		}
		catch (SQLException e)
		{
			handleException(e);
		}
	}
	
	public void addBatch() throws IScreenException
	{
		try
		{
			statement.addBatch();
		}
		catch (SQLException e)
		{
			handleException(e);
		}
	}

	private void closeRecordSet()
	{
		if (rs != null)
		{
			try
			{
				rs.close();
			}
			catch (SQLException e)
			{
			}
			rs = null;
		}
	}
	
	private void closeStatement()
	{
		try
		{
			statement.close();
		}
		catch (SQLException e)
		{
		}
	}
	
	public void open() throws IScreenException
	{
		closeRecordSet();
		
		try
		{
			rs = statement.executeQuery();
		}
		catch (SQLException e)
		{
			logger.error("SQL Failed: " + Constants.STR_NEW_LINE + sql);
			handleException(e);
	}
	}

	public int execute() throws IScreenException
	{
		return execute(true);
	}
	
	public int execute(boolean close) throws IScreenException
	{
		try
		{
			return statement.executeUpdate();
		}
		catch (SQLException e)
		{
			logger.error("SQL Failed: " + Constants.STR_NEW_LINE + sql);
			handleException(e);
			return 0;
		}
		finally
		{
			if (close)
			{
				closeStatement();
			}
		}
	}

	public int[] executeBatch() throws IScreenException
	{
		try
		{
			return statement.executeBatch();
		}
		catch (SQLException e)
		{
			logger.error("SQL Failed: " + Constants.STR_NEW_LINE + sql);
			handleException(e);
			return null;
		}
		finally
		{
			closeStatement();
		}
	}
	
	public void close()
	{
		closeRecordSet();
		closeStatement();
	}
	private void ensureRecordSetExist() throws IScreenException
	{
		if (rs == null)
			throw new IScreenException(IScreenExceptionCodes.INTERNAL_ERROR, "Record set does not exist.");
	}
	public boolean next() throws IScreenException
	{
		ensureRecordSetExist();
		try
		{
			return rs.next();
		}
		catch (SQLException e)
		{
			handleException(e);
			return false;
		}
	}

	public Object getObject(String fieldName) throws IScreenException
	{
		ensureRecordSetExist();
		try
		{
			return rs.getObject(fieldName);
		}
		catch (SQLException e)
		{
			handleException(e);
			return null;
		}
	}

	public String getString(String fieldName) throws IScreenException
	{
		return (String) getObject(fieldName);
	}
	
	private void handleException(SQLException e) throws IScreenException
	{
		errorHandler.handleException(e);
	}
}
